package cn.ym.basic.utils;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
/**
 * 获取IPV6地址
 *
 * @return
 */
public class Ipv6Utils {
    private static final Pattern IPV6_STD_PATTERN = Pattern.compile("^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}[/][0-9]{1,3}$");
    /**
     * 是否为IPV6地址
     * @param val 需要判断的地址内容.
     * @return
     */
    public static Boolean isIpv6(String val) {
        val = val.trim();
        if("".equals(val)) {
            return false;
        }

        String ipFull = "";
        try {
            ipFull = formatToExtend(val);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        if ("".equals(ipFull)) {
            return false;
        }

        return IPV6_STD_PATTERN.matcher(ipFull).find();
    }
    /**
     * 将任意一个ipv6地址转换成扩展格式.
     * @param sourceIp 源IP.
     * @return 扩展格式ipv6地址.
     */
    public static String formatToExtend(String sourceIp) {
        String[] columnArr = sourceIp.split("::");
        String behindPart = "";
        Integer behindColumnCount = 0;
        if (columnArr.length > 1) {
            behindPart = columnArr[1];
            String[] behindPartArr = behindPart.split(":");
            behindColumnCount = behindPartArr.length;
            if (behindPart.startsWith("/")) {
                behindPart = ":0000" + behindPart;
            } else {
                behindPart = ":" + behindPart;
            }
        }

        String prePart = columnArr[0] + ":0000:0000:0000:0000:0000:0000:0000:0000";
        String[] prePartArr = prePart.split(":");
        int i = 0;
        String preIp = "";
        for (String preColumn : prePartArr) {
            if (i < (8 - behindColumnCount)) {
                preColumn = "0000" + preColumn;
                preColumn = preColumn.substring(preColumn.length() -4);
                preIp += ("".equals(preIp) ? "" : ":") + preColumn;
            }
            i++;
        }

        return preIp + behindPart;
    }

    /**
     * 将任意一个ipv6地址转换为8位简写地址（姑且称为标准地址）.
     * @param sourceIp 源IP.
     * @return 8位简写ipv6地址.
     */
    public static String formatToStandard(String sourceIp) {
        String ipExtend = formatToExtend(sourceIp);
        // 省略前导0
        String[] array = ipExtend.split(":");
        List<String> tempList = new ArrayList<>();
        for (int i = 0; i < array.length; i++) {
            String res = array[i].replaceAll("^0+", "");
            if (res.equals("")){
                tempList.add("0");
            } else if (res.startsWith("/")) {
                tempList.add("0" + res);
            } else {
                tempList.add(res);
            }
        }
        return String.join(":", tempList);
    }

    /**
     * 将任意一个ipv6地址转成压缩地址.
     * @param sourceIp 源IP.
     * @return 压缩ipv6地址.
     */
    public static String formatToCompress(String sourceIp) {
        String ipStandard = formatToStandard(sourceIp);

        String mask = ipStandard.substring(ipStandard.lastIndexOf("/") + 1);
        String ip = ipStandard.substring(0, ipStandard.lastIndexOf("/"));

        // 压缩最长的一组 :0
        String[] array = ip.split(":");

        // 记录每组的连续0位置
        int[] cArray = new int[array.length];
        for (int i = 0; i < array.length; i++) {
            if ("0".equals(array[i])) {
                if (i > 0 && cArray[i - 1] > 0) {
                    cArray[i] = cArray[i - 1] + 1;
                } else {
                    cArray[i] = 1;
                }
            }
        }
        // 获取最长连续0的开始结束位置
        int endIdx = 0;
        int maxN = 0;
        for (int i = 0; i < cArray.length; i++) {
            if (cArray[i] > maxN) {
                endIdx = i;
                maxN = cArray[i];
            }
        }
        // 压缩最长连续0
        StringBuilder ipStr = new StringBuilder();
        int startIdx = endIdx - maxN + 1;
        for (int i = 0; i < array.length; i++) {
            if (startIdx == endIdx) {
                if (ipStr.length() > 0) {
                    ipStr.append(":");
                }
                ipStr.append(array[i]);
            } else {
                if (i < startIdx || i > endIdx) {
                    if (ipStr.length() > 0) {
                        ipStr.append(":");
                    }
                    ipStr.append(array[i]);
                } else if (i == startIdx) {
                    ipStr.append(":");
                } else if (i == endIdx) {
                    ipStr.append(":");
                }
            }
        }

        return ipStr.toString().replace(":::", "::") + "/" + mask;
    }



    /**
     * 16进制字符串转为2进制字符串.
     * @param source 16进制源.
     * @return 2进制结果.
     */
    public static String c16To2(String source) {
        Integer h = Integer.valueOf(source, 16);
        String ch = "0000000000000000" + Integer.toBinaryString(h);
        return ch.substring(ch.length() - 16);
    }

    /**
     * 2进制字符串转为16进制字符串.
     * @param source 2进制源.
     * @return 16进制结果.
     */
    public static String c2To16(String source) {
        Integer h = Integer.valueOf(source, 2);
        return Integer.toHexString(h);
    }

    /**
     * 10进制字符串转16进制字符串.
     * @param source
     * @return
     */
    public static String c10To16(String source) {
        Long c10 = Long.valueOf(source);
        return Long.toHexString(c10);
    }



    /**
     * 子网范围计算.
     * @param sourceIp ip + "/" + 子网位数.
     * @return String[] {起启IP, 结束IP}
     */
    public static String[] computeRange(String sourceIp) {
        //兼容windows表示格式
        sourceIp = sourceIp.replace("%", "/");
        sourceIp = formatToExtend(sourceIp);
        String ipArr[] = sourceIp.split("/");

        //IP部分(扩展格式).
        String ipPart = ipArr[0];
        Integer maskNumber = Integer.valueOf(ipArr[1]);

        //获取主机号
        String hostHex = "";
        String[] columnArr = ipPart.split(":");
        for (String ipColumn : columnArr) {
            String colHex = c16To2(ipColumn);
            hostHex += colHex;
        }

        // 获取网络号部分
        String netPart = hostHex.substring(0, maskNumber);

        //获得起始ip和结束ip
        String startIp = netPart;
        String endIp = netPart;
        for (int j = netPart.length(); j < 128; j++) {
            startIp += 0;
            endIp += 1;
        }

        //hex to 16
        String startIpC16 = "";
        String endIpC16 = "";
        for (int m = 0; m < 128; m = m + 16) {
            String hexColStart = startIp.substring(m, m + 16);
            String c16ColStart = c2To16(hexColStart);
            startIpC16 += ("".equals(startIpC16) ? "" : ":") + c16ColStart;

            String hexColEnd = endIp.substring(m, m + 16);
            String c16ColEnd = c2To16(hexColEnd);
            endIpC16 += ("".equals(endIpC16) ? "" : ":") + c16ColEnd;
        }

        return new String[] {startIpC16, endIpC16};
    }

    private static Long getSubNetLen(Integer maskNumber) {
        Double length = Math.pow(2D, (128D - maskNumber));
        return length.longValue();
    }

    /**
     * 通过起始和终止IP计算子网值.
     * @param startIp 起始IP.
     * @param endIp 终止IP.
     * @return 子网值.
     */
    public static Integer getSubnetFromRange(String startIp, String endIp) {
        //先将IP转为扩展模式
        startIp = formatToExtend(startIp);
        endIp = formatToExtend(endIp);


        //计算差异位
        String[] startIpArr = startIp.split(":");
        String[] endIpArr = endIp.split(":");
        int i;
        for (i = 0; i < 8; i++) {
            if (!startIpArr[i].equals(endIpArr[i])) {
                break;
            }
        }

        //基础值
        Integer baseArr[] = new Integer[] {0, 16, 32, 48, 64, 80, 96, 112};
        Integer base = baseArr[i];


        //计算差异段网位
        String binStart = c16To2(startIpArr[i]);
        String binEnd = c16To2(endIpArr[i]);
        int m;
        for (m = 0; m < 16; m++) {
            String charStart = binStart.substring(m, m+1);
            String charEnd = binEnd.substring(m, m+1);
            if (!charStart.equals(charEnd)) {
                break;
            }
        }

        return base + m;
    }


    public static void main(String[] args) {
        String testIp = "15ba:db5::/64";
        //String testIp = "2409::7c00:bd5f:14b3:376e:e97d";
        String ipExtend = formatToExtend(testIp);
        System.out.println(ipExtend);

        String standardIp = formatToStandard(ipExtend);
        System.out.println(standardIp);
        System.out.println(formatToCompress(ipExtend));

        standardIp = "15ba:db5:0:0:0:df:0:0/64";

        //standardIp = "2001:0000:1010:CD30:1111:0000:1101:0000/64";

        String compressIp = formatToCompress(standardIp);
        System.out.println(compressIp);

        String fix = "2409:8c5e:5000:61::9/13";
        String[] range = computeRange(fix);
        System.out.println(computeRange("2409:8c50:2400:6:21::/128")[1]);

        System.out.print("start ip: ");
        System.out.println(range[0]);
        System.out.print("end ip: ");
        System.out.println(range[1]);
    }
}

